/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Node.h"
///#include "Creator.h"
#include <Converter.h>
#include <xml/Stream.h>
#include <logs/Log.h>
#include <AutoPtr.h>
#include <Gluck.h>
#include <resource/Manager.h>
//#include <lib3d/hard/Hardware.h>

namespace liba
{
namespace lib2d
{
namespace node
{

const Atom Node::node_name( "Node" );
const Atom Node::visibility_name( "Visibility" );

Node::Node( const Atom & name, Node * parent )
:	NodeBase( name ),
	parent( parent ),
	visibility( true )
{
	if( parent )
		pos_in_parent = parent->children.insert( parent->children.end(), this );
}

Node::~Node()
{
	clear();
	if( parent )
	{
		parent->children.erase( pos_in_parent );
		parent = 0;
	}
}

void Node::clear()
{
	while( !children.empty() )
	{
		Node * node = children.back();
		delete node;
	}
}

void Node::add_child( Node * node, bool change_world_pos ) 
{
/*	Matrix full_matrix = Matrix::identity();
	if ( !change_world_pos )
		full_matrix = node->get_full_matrix();

	if( node->get_parent() )
	{	
		node->get_parent()->children.erase( node->pos_in_parent );
		node->parent = 0; // Exception safety (std::bad_alloc threat in the next line)
	}
	
	Children::iterator it = children.insert( children.end(), node );
	node->pos_in_parent = it;
	node->parent = this;

	if ( !change_world_pos )
		node->set_full_matrix( full_matrix ); 
		*/
}

Node * Node::find_node(const Atom & name, bool recursion )
{
	if( name == this->name ) 
	{
		return this;
	}
	Children::const_iterator it = children.begin();
	for( ; it != children.end(); ++it )
	{
		if( (*it)->get_name() == name )
			return *it;
		if ( recursion )
		{
			Node * node = (*it)->find_node( name, recursion );
			if ( node )
				return node;
		}
	}
	return 0;
}

bool Node::render( hard::Renderer * hardware )//, const CameraDesc & camera_desc )
{
	Children::iterator it = children.begin();
	for ( ; it != children.end(); ++it )
	if( (*it)->visibility )
	{
///		hardware->push_world_matrix_mult( (*it)->get_matrix() );
///		(*it)->render( hardware, camera_desc );
///		hardware->pop_world_matrix();
	}
	return true;
}
/*
void Node::render_to_list( hard::Hardware * hardware, hard::RenderList * render_list )
{
	Children::iterator it = children.begin();
	for ( ; it != children.end(); ++it )
	if( (*it)->visibility )
	{
		(*it)->total_matrix = total_matrix * (*it)->get_matrix();
		(*it)->render_to_list( hardware, render_list );
	}
}
*/
animate::Node * Node::find_animate_node( const Atom & name, bool recursion )
{
	Children::iterator it = children.begin();
	for( ; it != children.end(); ++it )
	{
		if( (*it)->get_name() == name )
			return *it;
		if ( recursion )
		{
			animate::Node * node = (*it)->find_animate_node( name, recursion );
			if ( node )
				return node;
		}
	}
	return 0;
}
/*
Matrix Node::get_full_matrix()const
{
	Matrix mat = get_matrix();
	Node * par = parent;

	for( ; par ; par = par->get_parent() )
		mat = par->get_matrix() * mat;
	return mat;
}

void Node::set_full_matrix( const Matrix & mat )
{
	set_matrix( Matrix::invert( parent->get_full_matrix() ) * mat );
}
*/

bool Node::get_attribute( const Atom & name, double * value )const
{
	if( name == visibility_name )
	{
		*value = double( visibility );
		return true;
	}
	return NodeBase::get_attribute( name, value );
}


bool Node::change_attribute( const Atom & name, double value )
{
	if( name == visibility_name )
	{
		visibility = (value > 0.5);
		return true;
	}
	return NodeBase::change_attribute( name, value );
}

bool Node::on_attribute( xml::Provider * prov, const String & name, const String & value )
{
	if ( name == "Visibility" )
	{
		return converter::convert( value, &visibility );
	}
	if ( name == "Children" )
	{
		AutoPtr<resource::Resource> res = resource::Manager::create_resource( value );
		return xml::StreamResource( res ).load( &xml::Root(this, "Scene") );
	}
	return NodeBase::on_attribute( prov, name, value );
}

bool Node::on_node( xml::Provider * prov, const String & name )
{
/*	Node * child = Factory::create( this, Atom( name ) );
	if( child )
		return prov->parse_object( children.back() );
		*/
	return false;
}
/*
void Node::change_rotation( const Vector & target_pos, const Vector & top )
{
	rotation = Vector::zero();
	Matrix parent_mat = get_parent()->get_full_matrix();
	Matrix inv_parent_mat = Matrix::invert(parent_mat);

	Vector dir = inv_parent_mat * target_pos - position;
	dir.normalize();
	Vector top_dir = top * inv_parent_mat;
	top_dir.normalize();
	Vector right = Vector::cross( dir, top_dir );
	right.normalize();
	top_dir = Vector::cross( right, dir );

	Matrix mat = Matrix::identity();
	mat.rc(0,0) = right.x();
	mat.rc(1,0) = right.y();
	mat.rc(2,0) = right.z();
	mat.rc(0,1) = top_dir.x();
	mat.rc(1,1) = top_dir.y();
	mat.rc(2,1) = top_dir.z();
	mat.rc(0,2) = dir.x();
	mat.rc(1,2) = dir.y();
	mat.rc(2,2) = dir.z();

	rotation = Matrix::factorization( mat );
	rotation.x() = 3.1415926f - rotation.x();
	dirty_matrix = true;
}
*/
/*
Node * create_from_resource( const Atom & name, Node * parent, const filesystem::String & resource_name )
{
	Node * nod = new Node( name, parent );
	AutoPtr<resource::Resource> res = resource::Manager::create_resource( resource_name );
	if( !xml::StreamResource( res ).load( nod ) )
	{
		delete nod; nod = 0;
		throw GluckBadResource(__FUNCTION__, "error parsing resource", resource_name );
	}
	return nod;
}
*/
} // namespace node
} // namespace lib2d
} // namespace liba

